import 'package:alice/extensions/extensions.dart';
import 'package:alice/widgets/gaps.dart';
import 'package:flutter/material.dart';

const double _kCircularIndicatorDiameter = 56;
const double _kIndicatorHeight = 32;

class SuperNavigationRail extends StatefulWidget {
  const SuperNavigationRail({
    super.key,
    required this.selectedIndex,
    required this.destinations,
    this.bottomDestinations,
    this.leading,
    this.trailing,
    this.onDestinationSelected,
    this.useIndicator,
  });

  final int? selectedIndex;

  final Widget? leading;

  final Widget? trailing;

  final ValueChanged<int>? onDestinationSelected;

  final List<NavigationRailDestination> destinations;
  final List<NavigationRailDestination>? bottomDestinations;

  final bool? useIndicator;

  @override
  State<SuperNavigationRail> createState() => _SuperNavigationRailState();
}

class _SuperNavigationRailState extends State<SuperNavigationRail> {

  @override
  Widget build(BuildContext context) {
    NavigationRailThemeData navigationRailTheme = context.theme.navigationRailTheme;
    final MaterialLocalizations localizations = MaterialLocalizations.of(context);
    final bool isRTLDirection = Directionality.of(context) == TextDirection.rtl;
    final double elevation = navigationRailTheme.elevation ?? 0.0;
    final double minWidth = navigationRailTheme.minWidth ?? 80.0;
    final verticalSpacing = 8.0;
    final bool useIndicator = widget.useIndicator ?? navigationRailTheme.useIndicator ?? true;
    final Color? indicatorColor = navigationRailTheme.indicatorColor;
    final ShapeBorder? indicatorShape = navigationRailTheme.indicatorShape;
    IconThemeData selectedIconTheme = navigationRailTheme.selectedIconTheme ?? IconThemeData(size: 24.0);
    IconThemeData unselectedIconTheme = navigationRailTheme.unselectedIconTheme ?? IconThemeData(size: 24.0);
    final tabCount = widget.destinations.length + (widget.bottomDestinations?.length ?? 0);
    return Semantics(
      explicitChildNodes: true,
      child: Material(
        elevation: elevation,
        color: navigationRailTheme.backgroundColor,
        child: SafeArea(
          right: isRTLDirection,
          left: !isRTLDirection,
          child: Column(
            children: [
              VGap(verticalSpacing),
              if (widget.leading != null) ...<Widget>[
                widget.leading!,
                VGap(verticalSpacing),
              ],
              Expanded(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    for (int i = 0; i < widget.destinations.length; i += 1)
                      _RailDestination(
                        minWidth: minWidth,
                        selected: widget.selectedIndex == i,
                        icon: widget.selectedIndex == i
                            ? widget.destinations[i].selectedIcon
                            : widget.destinations[i].icon,
                        label: widget.destinations[i].label,
                        iconTheme: widget.selectedIndex == i ? selectedIconTheme : unselectedIconTheme,
                        padding: widget.destinations[i].padding,
                        useIndicator: useIndicator,
                        indicatorColor: useIndicator ? indicatorColor : null,
                        indicatorShape: useIndicator ? indicatorShape : null,
                        onTap: () {
                          if (widget.onDestinationSelected != null) {
                            widget.onDestinationSelected!(i);
                          }
                        },
                        indexLabel: localizations.tabLabel(
                          tabIndex: i + 1,
                          tabCount: tabCount,
                        ),
                        disabled: widget.destinations[i].disabled,
                      ),
                    if (widget.trailing != null) widget.trailing!,
                    Spacer(),
                    if (widget.bottomDestinations != null)
                      for (int i = 0; i < widget.bottomDestinations!.length; i += 1)
                        _RailDestination(
                          minWidth: minWidth,
                          selected: widget.selectedIndex == widget.destinations.length + i,
                          icon: widget.selectedIndex == i
                              ? widget.bottomDestinations![i].selectedIcon
                              : widget.bottomDestinations![i].icon,
                          label: widget.bottomDestinations![i].label,
                          iconTheme: widget.selectedIndex == i ? selectedIconTheme : unselectedIconTheme,
                          padding: widget.bottomDestinations![i].padding,
                          useIndicator: useIndicator,
                          indicatorColor: useIndicator ? indicatorColor : null,
                          indicatorShape: useIndicator ? indicatorShape : null,
                          onTap: () {
                            if (widget.onDestinationSelected != null) {
                              widget.onDestinationSelected!(widget.destinations.length + i);
                            }
                          },
                          indexLabel: localizations.tabLabel(
                            tabIndex: widget.destinations.length + i + 1,
                            tabCount: tabCount,
                          ),
                          disabled: widget.bottomDestinations![i].disabled,
                        ),
                    VGap(verticalSpacing),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _RailDestination extends StatelessWidget {
  const _RailDestination({
    super.key,
    required this.minWidth,
    required this.selected,
    required this.icon,
    required this.label,
    required this.iconTheme,
    this.padding,
    required this.useIndicator,
    this.indicatorColor,
    this.indicatorShape,
    required this.onTap,
    required this.indexLabel,
    this.disabled = false,
  });

  final double minWidth;
  final bool selected;
  final Widget icon;
  final Widget label;
  final IconThemeData iconTheme;
  final EdgeInsetsGeometry? padding;
  final bool useIndicator;
  final Color? indicatorColor;
  final ShapeBorder? indicatorShape;
  final VoidCallback onTap;
  final String indexLabel;
  final bool disabled;

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    final TextDirection textDirection = Directionality.of(context);
    final bool material3 = theme.useMaterial3;
    final EdgeInsets destinationPadding = (padding ?? EdgeInsets.zero).resolve(textDirection);
    bool applyXOffset = false;
    final bool isLargeIconSize = iconTheme.size != null && iconTheme.size! > _kIndicatorHeight;
    final double indicatorVerticalOffset = isLargeIconSize ? (iconTheme.size! - _kIndicatorHeight) / 2 : 0;

    final Widget themedIcon = IconTheme(
      data: disabled ? iconTheme.copyWith(color: theme.colorScheme.onSurface.withOpacity(0.38)) : iconTheme,
      child: icon,
    );
    const double verticalDestinationSpacingM3 = 12.0;
    final indicatorOffset = Offset(
      minWidth / 2 + destinationPadding.left,
      verticalDestinationSpacingM3 / 2 + destinationPadding.top + indicatorVerticalOffset,
    );
    final Widget iconPart = Column(
      children: <Widget>[
        VGap(verticalDestinationSpacingM3 / 2),
        SizedBox(
          width: minWidth,
          child: _AddIndicator(
            addIndicator: useIndicator,
            indicatorColor: indicatorColor,
            indicatorShape: indicatorShape,
            isCircular: !material3,
            indicatorAnimation: AlwaysStoppedAnimation(1.0),
            child: themedIcon,
          ),
        ),
        VGap(verticalDestinationSpacingM3 / 2),
      ],
    );
    final Widget content = Padding(
      padding: padding ?? EdgeInsets.zero,
      child: Stack(
        children: <Widget>[
          iconPart,
          // For semantics when label is not showing,
          SizedBox.shrink(
            child: Visibility.maintain(
              visible: false,
              child: label,
            ),
          ),
        ],
      ),
    );

    final ColorScheme colors = Theme.of(context).colorScheme;
    return Semantics(
      container: true,
      selected: selected,
      child: Stack(
        children: <Widget>[
          Material(
            type: MaterialType.transparency,
            child: _IndicatorInkWell(
              onTap: disabled ? null : onTap,
              borderRadius: BorderRadius.all(Radius.circular(minWidth / 2.0)),
              customBorder: indicatorShape,
              splashColor: colors.primary.withOpacity(0.12),
              hoverColor: colors.primary.withOpacity(0.04),
              useMaterial3: material3,
              indicatorOffset: indicatorOffset,
              applyXOffset: applyXOffset,
              textDirection: textDirection,
              child: content,
            ),
          ),
          Semantics(
            label: indexLabel,
          ),
        ],
      ),
    );
  }
}


class _IndicatorInkWell extends InkResponse {
  const _IndicatorInkWell({
    super.child,
    super.onTap,
    ShapeBorder? customBorder,
    BorderRadius? borderRadius,
    super.splashColor,
    super.hoverColor,
    required this.useMaterial3,
    required this.indicatorOffset,
    required this.applyXOffset,
    required this.textDirection,
  }) : super(
    containedInkWell: true,
    highlightShape: BoxShape.rectangle,
    borderRadius: useMaterial3 ? null : borderRadius,
    customBorder: useMaterial3 ? customBorder : null,
  );

  final bool useMaterial3;

  // The offset used to position Ink highlight.
  final Offset indicatorOffset;

  // Whether the horizontal offset from indicatorOffset should be used to position Ink highlight.
  // If true, Ink highlight uses the indicator horizontal offset. If false, Ink highlight is centered horizontally.
  final bool applyXOffset;

  // The text direction used to adjust the indicator horizontal offset.
  final TextDirection textDirection;

  @override
  RectCallback? getRectCallback(RenderBox referenceBox) {
    if (useMaterial3) {
      final double boxWidth = referenceBox.size.width;
      double indicatorHorizontalCenter = applyXOffset ? indicatorOffset.dx : boxWidth / 2;
      if (textDirection == TextDirection.rtl) {
        indicatorHorizontalCenter = boxWidth - indicatorHorizontalCenter;
      }
      return () {
        return Rect.fromLTWH(
          indicatorHorizontalCenter - (_kCircularIndicatorDiameter / 2),
          indicatorOffset.dy,
          _kCircularIndicatorDiameter,
          _kIndicatorHeight,
        );
      };
    }
    return null;
  }
}

class _AddIndicator extends StatelessWidget {
  const _AddIndicator({
    required this.addIndicator,
    required this.isCircular,
    required this.indicatorColor,
    required this.indicatorShape,
    required this.indicatorAnimation,
    required this.child,
  });

  final bool addIndicator;
  final bool isCircular;
  final Color? indicatorColor;
  final ShapeBorder? indicatorShape;
  final Animation<double> indicatorAnimation;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    if (!addIndicator) {
      return child;
    }
    late final Widget indicator;
    if (isCircular) {
      indicator = NavigationIndicator(
        animation: indicatorAnimation,
        height: _kCircularIndicatorDiameter,
        width: _kCircularIndicatorDiameter,
        borderRadius: BorderRadius.circular(_kCircularIndicatorDiameter / 2),
        color: indicatorColor,
      );
    } else {
      indicator = NavigationIndicator(
        animation: indicatorAnimation,
        width: _kCircularIndicatorDiameter,
        shape: indicatorShape,
        color: indicatorColor,
      );
    }

    return Stack(
      alignment: Alignment.center,
      children: <Widget>[
        indicator,
        child,
      ],
    );
  }
}
